3.2-DOM 相关 API
Create by fall on 12 May 2020 Recently revised in 24 Feb 2024
拖放 API
使用拖放 API 需要注意:
<!-- 需要在能拖拽的 DOM 上放置 draggable="true" -->
<div id="draggable" draggable="true" class="">这个 div 可以拖动</div>
- ondragstart:元素开始被拖动时触发 作用在拖拽元素上
- ondragenter:当拖曳元素进入目标元素的时候触发的事件,作用在目标元素上
- ondragover:拖拽元素在目标元素上移动的时候触发的事件,作用在目标元素上
- ondragleave:拖拽元素拖离开了目标元素时触发,作用在目标元素上
- ondrop:被拖拽的元素在目标元素上同时鼠标放开触发的事件,作用在目标元素上
- ondragend:当拖拽完成后触发的事件,作用在被拖拽元素上
交叉监控 API
交叉监控 API,判断一个 DOM 是否出现在可视区域 view-port 上
Intersection Observer API
- 一般用于图片或其它内容的懒加载
- 无限滚动的网站
- 广告的可见度(可见度百分比以便计算收入)
- 根据可见度决定动画的执行
过去判断是否交叉,使用的是 Element.getBoundingClientRect()
不断地循环(可能导致性能问题)
let options = {
root: document.querySelector("#root"), // 用作视口的元素,用于检查目标的可见性,默认为浏览器视口
rootMargin: "0px", // 根周围的边距,默认全为零
threshold: 1.0, // 阈值:为 100% 时回调函数将会执行,默认为 0,只要有一个像素,都会执行
};
const callback = (ev) => {
console.log('callback-trigger',ev)
}
let observer = new IntersectionObserver(callback, options);
let target = document.querySelector("#规范");
observer.observe(target); // 要监听的 DOM
样式控制
使用 JS 来控制样式,可以通过用户编写的 CSS 样式,实现 CSS 效果,有以下三种方式
创建 CSSStyleSheet
创建 CSSStyleSheet 然后将内容挂载到 document 上
const stylesheet = new CSSStyleSheet();
stylesheet
.replace("body { font-size: 1.4em; } p { color: red; }")
.then(() => {
document.adoptedStyleSheets = [stylesheet] // 挂载到 document 上
})
.catch((err) => {
console.error("Failed to replace styles:", err);
});
style 元素标签
创建 style 元素标签,插入到 head 标签中,修改 InnerHTML
const styleEl = document.createElement("style");
document.head.appendChild(styleEl);
const stylesheet = new CSSStyleSheet();
styleEl.sheet = stylesheet;
styleEl.innerHTML = `#app {
font-size: 1.4em;
background-color: cyan;
height: 99px;
width: 99px
}
p {
color: red;
}`
修改 stylesheet 属性
创建 style 元素标签,然后获取 stylesheet 属性,修改该 stylesheet 属性
const styleEl = document.createElement("style");
document.head.appendChild(styleEl);
const stylesheet = styleEl.sheet;
stylesheet
.insertRule("#app {font-size: 1.4em;background-color:cyan;height:99px;width:99px; }", 0)
JS 动画
动画必须包含
- 有开始,有结束(必须保证每次设置一个定时器,必须关闭上一个定时器)
- 防止重复触发事件叠加(if & else 将运动和静止分隔开)
为什么 CSS 动画比 JavaScript 高效
- 一般,显示器刷新频率是 60HZ,即每秒更新 60 张图片,图片来自显卡的前缓冲区;
- 显卡的职责是合成新的图像,保存在后缓冲区,然后后缓冲区和前缓冲区互换,显卡更新频率和显示前刷新频率不一致,就会造成视觉上的卡顿;
- 渲染流水线生成的每一副图片称为一帧,生成一帧的方式有重排、重绘和合成三种;
- 重排会根据 CSSOM 和 DOM 计算布局树,重绘没有重新布局阶段;
- 生成布局树之后,渲染引擎根据布局树特点转化为层树,每一层解析出绘制列表;
- 栅格线程根据绘制列表中的指令生成图片,每一层对应一张图片,合成线程将这些图片合成一张图片,发送到后缓存区;
- 合成线程会将每个图层分割成大小固定的图块,优先绘制靠近视口的图块;
requestAnimationFrame
获取动画关键帧
若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 window.requestAnimationFrame()
const element = document.querySelector('.animate');
let start, previousTimeStamp;
let done = false
function step(timestamp) {
if (start === undefined) {
start = timestamp;
}
const elapsed = timestamp - start;
if (previousTimeStamp !== timestamp) {
// 这里使用 `Math.min()` 确保元素刚好停在 200px 的位置。
const count = Math.min(0.1 * elapsed, 200);
element.style.transform = 'translateX(' + count + 'px)';
if (count === 200) done = true;
}
if (elapsed < 2000) { // 在两秒后停止动画
previousTimeStamp = timestamp;
if (!done) {
window.requestAnimationFrame(step);
}
}
}
window.requestAnimationFrame(step);
Web Animations API
可以让我们用 JavaScript 写动画并且控制动画。
使用这些 API 进行开发时需要学习 CSS 的 animation、transform
一个动画示例
// 创建类似于 CSS 中的 @keyframes 的内容。
// Web 动画 API 不需要告知动画出现的百分比,有三个键的关键帧对象将通过动画的每个循环的方式播放中间键
// 必须至少指定两个关键帧(表示动画序列的开始和结束状态)
var aliceTumbling = [
{ transform: 'rotate(0) translate3D(-50%, -50%, 0)', color: '#000' },
{ backgroundColor: '#431236', offset: 0.3 }, // 可以直接在对象中指定一个偏移量
{ transform: 'rotate(360deg) translate3D(-50%, -50%, 0)', color: '#000' }
];
// 创建动画的一些属性,延迟,是否循环,同 css 的 animate 控制的内容
let aliceTiming = {
duration: 3000,
iterations: Infinity
}
// 第二个参数也可以选择 3000,当然也只会执行一次
document.querySelector("dom").animate(
aliceTumbling,
aliceTiming
)
在创建时,会返回一个控制器,
const controller = document.querySelector("dom").animate(
aliceTumbling,
aliceTiming
)
// 可以通过该控制器,控制动画的进程
controller.pause() // 暂停动画
controller.play(); // 执行内容
controller.finish() // 动画结束。
controller.cancel() // 终止动画。
// 控制动画的播放速度
controller.playbackRate = 0.4
// 控制当前动画的时间
controller.currentTime = 2000
controller.onfinish = () => {
console.log('lalalal')
}
MutationObserver
观察
var targetNode = document.querySelector("#someElement");
var observerOptions = {
childList: true, // 观察目标子节点的变化,是否有添加或者删除
attributes: true, // 观察属性变动
subtree: true, // 观察后代节点,默认为 false
attributeFilter:['id','class'] // 筛选观察 id 和 class 属性,不筛选就是默认全部
};
function callback(mutationList, observer) {
mutationList.forEach((mutation) => {
switch (mutation.type) {
case "childList":
/* 从树上添加或移除一个或更多的子节点;参见 mutation.addedNodes 与
mutation.removedNodes */
break;
case "attributes":
/* mutation.target 中某节点的一个属性值被更改;该属性名称在 mutation.attributeName 中,
该属性之前的值为 mutation.oldValue */
break;
}
});
}
var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
// 如果不想要监听
observer.disconnect()
参考文章
作者 | 链接 |
---|---|
MDN 官方文档 | https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API |
EvenyYlzz | https://juejin.cn/post/6900005459856457735 |